package com.zhny.aiot.three.ball.v2.four.plugin;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.plugin.v2.gateway.ZhnyGateWayDevice;
import com.zhny.aiot.plugin.v2.gateway.model.*;
import com.zhny.aiot.plugin.zhnygateway.GatewayEngine;
import com.zhny.aiot.plugin.zhnygateway.device.ZhnyConfig;
import com.zhny.aiot.plugin.zhnygateway.model.GatewayResponseMessage;
import com.zhny.aiot.plugin.zhnygateway.model.NodeRequestMessage;
import com.zhny.aiot.three.ball.v2.four.plugin.model.BallThreeValve4GV2StoreEntry;
import com.zhny.aiot.three.ball.v2.four.plugin.model.ComParam;
import com.zhny.aiot.three.ball.v2.four.plugin.model.Gateway4GStoreEntry;
import com.zhny.aiot.three.ball.v2.four.plugin.model.InputParam;
import io.netty.buffer.ByteBuf;
import org.pf4j.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class BallThreeValve4GV2Device extends ZhnyGateWayDevice<BallThreeValve4GV2StoreEntry> {
    private static final Logger logger=LoggerFactory.getLogger (BallThreeValve4GV2Device.class);
    private final Map<String, ComParam> CommandOperations;

    public BallThreeValve4GV2Device(GatewayEngine engine, IDeviceFactory<ZhnyConfig> factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super (engine, factory, centerFacilities, entity);
        CommandOperations=new ImmutableMap.Builder<String, ComParam> ()
                .put ("aValue", new ComParam (0x32, getzhnyType ()))
                .put ("bValue", new ComParam (0x33, getzhnyType ()))
//                .put ("upDownQuery", new ComParam (0x30, getzhnyType ()))
                .put ("query", new ComParam (0x39, getzhnyType ()))
                .put ("queryPressure", new ComParam (0x3B, getzhnyType ()))
                .put ("upValue", new ComParam (0x37, getzhnyType ()))
                .put ("downValue", new ComParam (0x38, getzhnyType ()))
                .put ("queryVoltage", new ComParam (0x3A, getzhnyType ()))
                .put ("queryFlow", new ComParam (0x4A, 25))
                .put ("queryTotalFlow", new ComParam (0x4C, 25))
                .put ("clear", new ComParam (0x4D, 25)).build ();
    }

    private void onPreDecoder(GatewayResponseMessage message){

        int type = message.getDeviceType ();
        if(type == 0x0A){
            logger.info("---start decoder gateway message---");
            onGatewayMessage (message);
        }
        if(message.getDeviceData() !=null && message.getDeviceOper () != 0x07)
            message.getDeviceData ().release ();
    }
    @Override
    public void onDecoder(GatewayResponseMessage message) {
        logger.info (message.toString ());
        int type=message.getDeviceType ();
        if(type == 0x0A){
            onPreDecoder(message);
        }
        if(type == 0x20) {
            logger.info ("---start decoder three 4g ball valve message---");
            onMessage (message);
            if(message.getDeviceData () != null)
                message.getDeviceData ().release ();
        }
    }

    private void onMessage(GatewayResponseMessage message) {
        MessageEntry resultMsg=MessageUtils.createMessageEntry (this.getId ());
        resultMsg.setType (MessageType.RESULT);
        BallThreeValve4GV2StoreEntry storeEntry=new BallThreeValve4GV2StoreEntry ();
        logger.info ("------three boll valve v2 decode start------");
        int operation=message.getDeviceOper ();
        if(operation>=0x31 && operation<=0x34) {
            if(message.getDataLength () == 7) {
                resultMsg.setType (MessageType.ERROR);
                resultMsg.setData ("水利机械阀加入任务失败");
                logger.error ("[deviceNo:{},controllerNo:{}] 水利机械阀加入任务[{}]失败"
                        , getConfig ().getAddress (), getConfig ().getChannelNo (), operation);
                nofity (resultMsg);
                return;
            }else if(operation == 0x31) {
                storeEntry.setA ("0");
                storeEntry.setB ("0");
                storeEntry.setVoltage (createVol (message.getDeviceData ().readShort ()));
                resultMsg.setData (storeEntry);
            }else if(operation == 0x32) {
                int value=message.getDeviceData ().readByte ();
                int vol=message.getDeviceData ().readUnsignedShort ();
                storeEntry.setA (String.valueOf (value));
                storeEntry.setB ("0");
                storeEntry.setVoltage (createVol (vol));
                resultMsg.setData (storeEntry);
            }else if(operation == 0x33) {
                int value=message.getDeviceData ().readByte ();
                int vol=message.getDeviceData ().readUnsignedShort ();
                storeEntry.setA ("0");
                storeEntry.setB (String.valueOf (value));
                storeEntry.setVoltage (createVol (vol));
                resultMsg.setData (storeEntry);
            }
        }else if(operation == 0x35) {
            int range=message.getDeviceData ().readByte ();
            int value=message.getDeviceData ().readByte ();
            if(range == 0) { /*00表示A通道压力小于压力下限值*/
                storeEntry.setAPressure (String.format ("压力低！当前%s", value));
                storeEntry.setBPressure ("0");
            }else if(range == 1) {  /*01表示A通道压力大于压力上限值*/
                storeEntry.setAPressure (String.format ("压力大！当前%s", value));
                storeEntry.setBPressure ("0");
            }else if(range == 16) { /*10表示B通道压力小于压力下限值*/
                storeEntry.setBPressure (String.format ("压力低！当前%s", value));
                storeEntry.setAPressure ("0");
            }else if(range == 17) { /*11表示B通道压力大于压力上限值*/
                storeEntry.setBPressure (String.format ("压力大！当前%s", value));
                storeEntry.setAPressure ("0");
            }
            resultMsg.setData (storeEntry);
        }else if(operation == 0x37) {
            int maxp=message.getDeviceData ().readByte ();
            storeEntry.setPressureUp (String.valueOf (maxp));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x38) {
            int maxp=message.getDeviceData ().readByte ();
            storeEntry.setPressureUp (String.valueOf (maxp));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x39) {
            int aValue=message.getDeviceData ().readByte () & 0xff;
            int bValue=message.getDeviceData ().readByte () & 0xff;
            String vol=createVol (message.getDeviceData ().readUnsignedShort ());
            if(aValue != 255)
                storeEntry.setA (String.valueOf (aValue));
            if(bValue != 255)
                storeEntry.setB (String.valueOf (bValue));
            storeEntry.setVoltage (vol);
            resultMsg.setData (storeEntry);
        }else if(operation == 0x3A) {
            storeEntry.setVoltage (createVol (message.getDeviceData ().readUnsignedShort ()));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x3B) {
            storeEntry.setAPressure (createPressure (message.getDeviceData ().readByte ()));
            storeEntry.setBPressure (createPressure (message.getDeviceData ().readByte ()));
            storeEntry.setVoltage (createVol (message.getDeviceData ().readUnsignedShort ()));
            resultMsg.setData (storeEntry);
        }
        else if(operation == 0x4A){
            storeEntry = new BallThreeValve4GV2StoreEntry();
            storeEntry.setFlow (createFlow(message.getDeviceData ()));
            resultMsg.setData (storeEntry);
        }else if(operation == 0x4C){
            storeEntry = new BallThreeValve4GV2StoreEntry();
            storeEntry.setTotalFlow (createTotalFlow(message.getDeviceData ()));
            resultMsg.setData (storeEntry);
        }
        else if(operation == 0x4D){
            storeEntry = new BallThreeValve4GV2StoreEntry();
            storeEntry.setClearDate (new SimpleDateFormat ("yyyy-MM-dd").format (new Date ()));
            resultMsg.setData (storeEntry);
        }
        logger.info ("4G three ball valve store[{}]", storeEntry.toString ());
        save (storeEntry);
        saveReal (storeEntry);
        nofity (resultMsg);
    }

    private String createPressure(int pressure) {
        double result=pressure / 100.00;
        return String.format ("%.2f", result);
    }

    private String createVol(int vol) {
//        V=(VH*256+VL)/1000
        Double value=vol / 1000.00;
        String result=value<=6 ? String.format ("电压过低!当前值%.2f", value) : String.format ("%.2f", value);
        return result;
    }

    private String createTotalFlow(ByteBuf buf) {
        int totalFlow1=((buf.readByte() << 24) &0xff000000|
                (buf.readByte() << 16) &0x00ff0000|
                (buf.readByte() << 8) &0x0000ff00 |
                (buf.readByte()) & 0x000000ff);
        int totalFlow=((buf.readByte() << 24) &0xff000000|
                (buf.readByte() << 16) &0x00ff0000|
                (buf.readByte() << 8) &0x0000ff00 |
                (buf.readByte()) & 0x000000ff);
        totalFlow = totalFlow1 + totalFlow;
        if (totalFlow <= 0)
            return "0.0";
        double value = totalFlow* 0.00067;
        return String.format("%.2f",value * 1.0);
    }

    private String createFlow(ByteBuf buf) {
        int flow=((buf.readByte() << 16) &0xff0000|
                (buf.readByte() << 8) &0x00ff00 |
                (buf.readByte()) & 0x0000ff);
        if (flow <= 0)
            return "0.0";
        double value = Math.sqrt (345 * flow + 1600)-40;
        return String.format("%.2f",value * 1.0);
    }


    @Override
    protected NodeRequestMessage onCommand(TaskEntity taskEntity) {
        InputParam param=null;
        if(StringUtils.isNullOrEmpty (taskEntity.getMethodName ()))
            return null;
        if(!StringUtils.isNullOrEmpty (taskEntity.getParams ())) {
            ObjectMapper mapper=new ObjectMapper ();
            mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            try {
                param=mapper.readValue (taskEntity.getParams (), InputParam.class);
            } catch (Exception e) {
                logger.error ("Parse param error !");
                return null;
            }
        }
        Map<String, Object> command=new HashMap<String, Object> ();
        command.put (taskEntity.getMethodName (), param);
        logger.info ("three ball valve command[{}] gprs[{}] address[{}]", taskEntity.getMethodName (),
                getConfig ().getGprsNo (), Integer.parseInt (getConfig ().getAddress ())
        );
        return encode (taskEntity.getKey (), command);
    }

    private NodeRequestMessage encode(long key, Map<String, Object> commandMap) {
        for (String id : commandMap.keySet ()){
            if(CommandOperations.containsKey (id)) {
                byte[] data=null;
                ComParam param=CommandOperations.get (id);
                Integer oper=param.getCommand ();
                Integer deviceType=param.getDeviceType ();
                Object obj=commandMap.get (id);
                if(obj != null) {
                    InputParam par=(InputParam) obj;
                    data=new byte[]{par.getValue ()};
                }
                int length=data != null ? data.length+4 : 4;
                return new NodeV2RequestMessage (deviceType+128, length, oper, Integer.parseInt (getConfig ().getAddress ()), key, data);
            }
        }
        return null;
    }

    @Override
    protected int getzhnyType() {
        return 32;
    }

    @Override
    protected void disposable() {

    }

    private void onGatewayMessage(GatewayResponseMessage msg) {
        GatewayV2ResponseMessage message = (GatewayV2ResponseMessage)msg;
        MessageEntry resultMsg = MessageUtils.createMessageEntry (this.getId ());
        GatewayResultEntry gatewayResult = new GatewayResultEntry ();
        resultMsg.setType(MessageType.RESULT);
        Gateway4GStoreEntry storeEntry = new Gateway4GStoreEntry ();
        int operation = message.getDeviceOper ();
        GatewayTaskType taskType = message.getTaskType () == 1?
                GatewayTaskType.INSTANT_BATCH:message.getTaskType () == 2?
                GatewayTaskType.INSTANT_BATCH:message.getTaskType () == 3?
                GatewayTaskType.CRON_IRRIGATION:message.getTaskType () == 4?
                GatewayTaskType.TIME_IRRIGATION:message.getTaskType () == 5?
                GatewayTaskType.INSTANT_IRRIGATION:GatewayTaskType.OTHER;
        if(operation >= 0x01 && operation <= 0x0f){
            String key = getConfigKey();
            if(operation == 0x01){
                String describe = String.format("协调器%s数据打包成功",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.EXECUTING);
                gatewayResult.setType (taskType);
                gatewayResult.setDeviceId (this.getId ());
            }else if(operation == 0x02){
                String describe = String.format("协调器%s接收通信失败设备编号统计",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (getSignalFail (message.getDeviceData ()));
                gatewayResult.setStatus (GatewayResultStatus.FAIL_COMPLETED);
                gatewayResult.setType (taskType);
                gatewayResult.setDeviceId (this.getId ());
            }else if(operation == 0x04){
                String describe = String.format("协调器%s数据接收校验失败",key);
                storeEntry.setDescription (describe);
                resultMsg.setType(MessageType.ERROR);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.ERROR);
                gatewayResult.setType (taskType);
                gatewayResult.setDeviceId (this.getId ());
            }else if(operation == 0x05){
                String describe = String.format("协调器%s接收数据包太大",key);
                storeEntry.setDescription (describe);
                resultMsg.setType(MessageType.ERROR);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.ERROR);
                gatewayResult.setType (taskType);
                gatewayResult.setDeviceId (this.getId ());
            }else if(operation == 0x06){
                String describe = String.format("协调器%s当前任务操作完成",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.COMPLETED);
                gatewayResult.setType (taskType);
            }else if(operation == 0x07){
                String describe = String.format("协调器%s终端设备回复成功",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.NODE_REPLY_SUCCESS);
                gatewayResult.setType (taskType);
            }else if(operation == 0x08){
                String describe = String.format("协调器%s当前任务操作流程已完成",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.OVER_COMPLETED);
                gatewayResult.setType (taskType);
            }else if(operation == 0x0b){
                int deviceNo = message.getDeviceData().readUnsignedShort();
                String describe = String.format("控制器%s通信失败",deviceNo);
                storeEntry.setDescription (describe);
                resultMsg.setType(MessageType.ERROR);
                resultMsg.setData (storeEntry);
                gatewayResult.setKey (message.getTaskKey ());
                gatewayResult.setDescription (describe);
                gatewayResult.setStatus (GatewayResultStatus.FAILED);
                gatewayResult.setType (taskType);
                gatewayResult.setDeviceId (this.getId ());
            }else if(operation == 0x0f){
                String describe = String.format("协调器%s重启成功",key);
                storeEntry.setDescription (describe);
                resultMsg.setData (storeEntry);
            }
        }
        logger.info("gateway message store[{}]",storeEntry.getDescription());
        logger.info("gateway result message [{}]", gatewayResult.toString ());
//        save (storeEntry);
//        nofity (resultMsg);
        saveResult (gatewayResult);
    }

    private String getSignalFail(ByteBuf buf){
        int len =buf.readableBytes ();
        StringBuilder builder = new StringBuilder ();
        for (int i= 0; i < len/2; i++){
            int deviceNo = buf.readUnsignedShort ();
            builder.append (deviceNo);
            if(i != len/2-1)
                builder.append (",");
        }
        return builder.toString ();
    }
}
